<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.ico">
  <link rel="mask-icon" href="/images/favicon.ico" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Mist","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="Mina心跳检测实现">
<meta property="og:type" content="article">
<meta property="og:title" content="Mina心跳检测实现">
<meta property="og:url" content="http://yoursite.com/2018/07/24/tech-2018-07-24-MinaHeartbeat/index.html">
<meta property="og:site_name" content="Akiyama&#39;s Blog">
<meta property="og:description" content="Mina心跳检测实现">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://yoursite.com/images/Mina/mina_heartbeat01.png">
<meta property="og:image" content="http://yoursite.com/images/Mina/mina_heartbeat02.png">
<meta property="article:published_time" content="2018-07-23T16:00:00.000Z">
<meta property="article:modified_time" content="2021-05-16T09:46:29.733Z">
<meta property="article:author" content="丘山月夜">
<meta property="article:tag" content="技术">
<meta property="article:tag" content="网络编程">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://yoursite.com/images/Mina/mina_heartbeat01.png">

<link rel="canonical" href="http://yoursite.com/2018/07/24/tech-2018-07-24-MinaHeartbeat/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Mina心跳检测实现 | Akiyama's Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Akiyama's Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">丘山月夜的博客</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="home fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/07/24/tech-2018-07-24-MinaHeartbeat/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.png">
      <meta itemprop="name" content="丘山月夜">
      <meta itemprop="description" content="君は空を見てるか,<br>風の音を聞いてるか">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Akiyama's Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Mina心跳检测实现
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2018-07-24 00:00:00" itemprop="dateCreated datePublished" datetime="2018-07-24T00:00:00+08:00">2018-07-24</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-05-16 17:46:29" itemprop="dateModified" datetime="2021-05-16T17:46:29+08:00">2021-05-16</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/" itemprop="url" rel="index"><span itemprop="name"></span></a>
                </span>
            </span>

          
            <div class="post-description">Mina心跳检测实现</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>上一篇文章讲了Mina的简单使用，这一篇将要讲讲怎么用Mina实现心跳检测。网上有很多相关的文章，但是我觉得比较难理解，自己折腾了好久才明白，所以想用我觉得容易理解的话总结一下。也给自己做个笔记。</p>
<h2 id="一、心跳机制"><a href="#一、心跳机制" class="headerlink" title="一、心跳机制"></a>一、心跳机制</h2><p>​    1.心跳机制有什么用？<br>在TCP的长连接中，有可能两端有很长一段时间没有数据往来，理论上连接应该是一直保持的。但实际情况中，如果中间节点出现故障，连接断开（如防火墙，或者断网等），这时候故障是难以知道的。</p>
<p>比如客户端因为某种情况断开了连接，服务端并不会收到消息，可能还傻傻的等待对方发送消息过来。反过来也一样。</p>
<p>所以发明了心跳机制来维持长连接，保活。</p>
<p>​    2.心跳机制是什么？</p>
<p>心跳机制原理很简单，就是客户端每隔一段时间发送一个“请求心跳包”到服务端，服务端收到“请求心跳包”之后回复一个心跳包，客户端接收到“回复心跳包”后判断连接正常。</p>
<p>如果客户端“在一定时间（即超时时间）”没有收到回复包则表示中间出错了，可以尝试重新连接或者做出相应的操作。</p>
<p>当然一般我们会设置一个变量，“多次判断”有没有收到回复包。比如3次没收到回复包，才判断连接出错。如果收到了回复包，则把变量重置为0。</p>
<p>由谁（客户端还是服务端）发起请求其实都一样，当然也可以两边都发，具体情况看需求和方便性。比如为了减轻服务端压力，由客户端发出请求较合适。</p>
<p><img src="/images/Mina/mina_heartbeat01.png" alt="mina心跳包01"></p>
<p>​    3.心跳包是什么？</p>
<p>心跳包其实就是双方规定的一个信息，可以是一个数字、一串字符、也可以是一个结构对象，只要接收方能够判断就行。</p>
<p>我把发出请求的心跳包叫做心跳请求包，把回复的心跳包叫做心跳回复包。</p>
<p>心跳包之所以叫心跳包是因为：他像心跳一样每隔一定时间发送一次，以此告诉对方，自己还活着。</p>
<h2 id="二、Mina实现心跳机制"><a href="#二、Mina实现心跳机制" class="headerlink" title="二、Mina实现心跳机制"></a>二、Mina实现心跳机制</h2><p>​    上一篇文章讲了Mina的简单使用，我们通过设置IoHandler来处理业务逻辑，IoHandler中有messageReceived方法，当接收到消息时回调此方法。</p>
<p>​    <strong>1.很容易想到的方法</strong></p>
<p>所以我们很容易想到，只要在任何一端（假设为A端）设置一个定时器，定时发送一个心跳请求包。然后在另一端（假设为B端）的messageReceived方法中判断接收到的消息是不是心跳请求包。如果是，则发出一个心跳回复包，同样在A端的messageReceived方法中判断接收到的消息是不是一个心跳回复包。如果是则判断连接成功。</p>
<p>​    <strong>2.定时器</strong></p>
<p>上面说的思路肯定是能够实现的，现在我们来说下这个定时器怎么设置。</p>
<p>上一篇文章中还提到了一个函数</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, <span class="number">10</span>);</span><br></pre></td></tr></table></figure>

<p>这个函数会在判断当前Session为闲置状态时，每隔10S调用一次IoHandler的sessionIdle方法，因此我们可以使用这个函数设置合适的时间，在sessionIdle中发出心跳请求包，就不用自己写一个定时器了。</p>
<p>​    <strong>3.这么容易满足吗？</strong></p>
<p>上面说的已经能够实现正常的心跳机制了。但是把所有的代码和判断都放在IoHandler的业务逻辑处理中，是否不够优雅？</p>
<p>在上篇文章中，我们提到了消息是通过IoService接收，通过层层拦截器IoFilterChain，然后才传递到IoHandler中进行业务逻辑处理，处理之后的发送也需要通过层层拦截器才能发送出去。</p>
<p>因此我们想心跳包检测要是能在拦截器阶段就被消费掉，是否就不用传递到IoHandler中了，IoHandler就可以只处理业务逻辑，将心跳检测独立出来。</p>
<p>很庆幸，Mina已经将这个拦截器给我们封装好了，我们只需要知道如何使用即可。</p>
<p>​    <strong>4.Mina的心跳检测拦截器/过滤器</strong></p>
<p><strong>创建拦截器：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">            <span class="comment">//实现KeepAliveMessageFactory接口方法</span></span><br><span class="line">            HeartRequestFactoryImpl heartRequestFactory = <span class="keyword">new</span> HeartRequestFactoryImpl();</span><br><span class="line">            <span class="comment">//创建拦截器，第一个参数是心跳处理接口，第二个参数是心跳超时处理接口</span></span><br><span class="line">            KeepAliveFilter kaf = <span class="keyword">new</span> KeepAliveFilter(heartRequestFactory, <span class="keyword">new</span> KeepAliveRequestTimeoutHandlerImpl());</span><br><span class="line">            <span class="comment">//设置请求间隔，单位s</span></span><br><span class="line">            kaf.setRequestInterval(<span class="number">20</span>);</span><br><span class="line">            <span class="comment">//设置超时时间</span></span><br><span class="line">            kaf.setRequestTimeout(<span class="number">10</span>);</span><br><span class="line">            <span class="comment">//设置是否forward到下一个filter,默认为false</span></span><br><span class="line"><span class="comment">//            kaf.setForwardEvent(true);</span></span><br><span class="line">            acceptor.getFilterChain().addLast(<span class="string">&quot;heart&quot;</span>, kaf);</span><br></pre></td></tr></table></figure>


<p><strong>需要注意的是：</strong></p>
<p>（1）客户端也需要添加心跳检测拦截器，只不过KeepAliveMessageFactory实现方法不同</p>
<p>（2）由服务端发出心跳请求包的话，客户端不需要实现超时处理方法，即KeepAliveRequestTimeoutHandler。超时方法是一定时间内没有收到“心跳回复包”才调用的，而客户端只会收到心跳请求包，不会收到心跳回复包</p>
<p>我们需要做的主要是实现KeepAliveMessageFactory接口方法</p>
<p>有四个方法，isRequest，isResponse，getRequest，getResponse需要我们实现，这些方法具体作用如下：</p>
<p>isRequest：判断接收到的消息是不是心跳请求包<br>isResponse：判断接收到的消息是不是心跳回复包<br>getRequest：获取心跳请求包<br>getResponse：获取心跳回复包</p>
<p><strong>关键要理解这些方法返回值的含义和调用时机：</strong></p>
<p>以服务端发送心跳请求包为例（反过来也一样），流程如下：<br><img src="/images/Mina/mina_heartbeat02.png" alt="mina心跳包02"></p>
<p>可以看到，客户端和服务端都需要创建拦截器，实现KeepAliveMessageFactory接口方法，只不过写法不一样。其他的发送消息流程并不需要关心</p>
<p>除此之外，发送请求包的一方还可以实现超时处理接口，如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">KeepAliveRequestTimeoutHandlerImpl</span> <span class="keyword">implements</span> <span class="title">KeepAliveRequestTimeoutHandler</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span><span class="comment">//在超时时间范围内没有收到消息会调用此方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">keepAliveRequestTimedOut</span><span class="params">(KeepAliveFilter keepAliveFilter, IoSession ioSession)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;心跳超时！&quot;</span>);<span class="comment">//做出相应处理</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<p><strong>分析源码：</strong></p>
<p>心跳拦截器KeepAliveFliter源码，关键部分如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageReceived</span><span class="params">(NextFilter nextFilter, IoSession session, Object message)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">this</span>.messageFactory.isRequest(session, message)) &#123;</span><br><span class="line">            Object pongMessage = <span class="keyword">this</span>.messageFactory.getResponse(session, message);</span><br><span class="line">            <span class="keyword">if</span>(pongMessage != <span class="keyword">null</span>) &#123;</span><br><span class="line">                nextFilter.filterWrite(session, <span class="keyword">new</span> DefaultWriteRequest(pongMessage));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">this</span>.messageFactory.isResponse(session, message)) &#123;</span><br><span class="line">            <span class="keyword">this</span>.resetStatus(session);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(!<span class="keyword">this</span>.isKeepAliveMessage(session, message)) &#123;</span><br><span class="line">            nextFilter.messageReceived(session, message);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">messageSent</span><span class="params">(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    Object message = writeRequest.getMessage();</span><br><span class="line">    <span class="keyword">if</span>(!<span class="keyword">this</span>.isKeepAliveMessage(session, message)) &#123;</span><br><span class="line">        nextFilter.messageSent(session, writeRequest);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sessionIdle</span><span class="params">(NextFilter nextFilter, IoSession session, IdleStatus status)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(status == <span class="keyword">this</span>.interestedIdleStatus) &#123;</span><br><span class="line">        <span class="keyword">if</span>(!session.containsAttribute(<span class="keyword">this</span>.WAITING_FOR_RESPONSE)) &#123;</span><br><span class="line">            Object pingMessage = <span class="keyword">this</span>.messageFactory.getRequest(session);</span><br><span class="line">            <span class="keyword">if</span>(pingMessage != <span class="keyword">null</span>) &#123;</span><br><span class="line">                nextFilter.filterWrite(session, <span class="keyword">new</span> DefaultWriteRequest(pingMessage));</span><br><span class="line">                <span class="keyword">if</span>(<span class="keyword">this</span>.getRequestTimeoutHandler() != KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) &#123;</span><br><span class="line">                    <span class="keyword">this</span>.markStatus(session);</span><br><span class="line">                    <span class="keyword">if</span>(<span class="keyword">this</span>.interestedIdleStatus == IdleStatus.BOTH_IDLE) &#123;</span><br><span class="line">                        session.setAttribute(<span class="keyword">this</span>.IGNORE_READER_IDLE_ONCE);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">this</span>.resetStatus(session);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>.handlePingTimeout(session);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span>(status == IdleStatus.READER_IDLE &amp;&amp; session.removeAttribute(<span class="keyword">this</span>.IGNORE_READER_IDLE_ONCE) == <span class="keyword">null</span> &amp;&amp; session.containsAttribute(<span class="keyword">this</span>.WAITING_FOR_RESPONSE)) &#123;</span><br><span class="line">        <span class="keyword">this</span>.handlePingTimeout(session);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.forwardEvent) &#123;</span><br><span class="line">        nextFilter.sessionIdle(session, status);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>（1）我们设置了setRequestInterval，每隔一定时间就会调用sessionIdle方法，该方法最主要的就是下面几句：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Object pingMessage = <span class="keyword">this</span>.messageFactory.getRequest(session);<span class="comment">//调用getRequest获得返回值</span></span><br><span class="line"><span class="keyword">if</span>(pingMessage != <span class="keyword">null</span>) &#123;                                        <span class="comment">//如果有返回值</span></span><br><span class="line">    nextFilter.filterWrite(session, <span class="keyword">new</span> DefaultWriteRequest(pingMessage));<span class="comment">//将该该返回值发送出去，因此该返回值表示的含义就是心跳请求包</span></span><br><span class="line">    …………                 </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<p>即如果getRequest方法设置了返回值，就会将该返回值发送出去，所以这个返回值的意义就是“心跳请求包”。</p>
<p>（2）客户端收到消息，会调用拦截器的messageReceived方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.messageFactory.isRequest(session, message)) &#123;<span class="comment">//我们在isRequest方法中判断接收到的是不是心跳请求包，如果是返回为true</span></span><br><span class="line">        Object pongMessage = <span class="keyword">this</span>.messageFactory.getResponse(session, message);<span class="comment">//然后调用getResponse方法获得返回值</span></span><br><span class="line">        <span class="keyword">if</span>(pongMessage != <span class="keyword">null</span>) &#123;                                                <span class="comment">//如果有返回值</span></span><br><span class="line">            nextFilter.filterWrite(session, <span class="keyword">new</span> DefaultWriteRequest(pongMessage));<span class="comment">//则将该返回值发送出去，因此该返回值的意义就是心跳回复包</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">this</span>.messageFactory.isResponse(session, message)) &#123;<span class="comment">//我们在isResponse方法中判断接收到的是不是心跳回复包，如果是返回为true</span></span><br><span class="line">        <span class="keyword">this</span>.resetStatus(session);                        <span class="comment">//接收到了心跳回复包，需要重置状态，如重新开始计时</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(!<span class="keyword">this</span>.isKeepAliveMessage(session, message)) &#123;<span class="comment">//该方法执行了一遍isRequest和isResponse，如果接收到的消息既不是心跳请求包，也不是回复包</span></span><br><span class="line">        nextFilter.messageReceived(session, message);<span class="comment">//则将该消息往下传递，即交给下个拦截器或者交给Handler的messageReceive方法处理</span></span><br><span class="line">    &#125;<span class="comment">//如果接收到的消息是心跳请求包“或者”心跳回复包，则在上面几句中就已经将该消息消费掉了</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<p>注意上面还有一句：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">            <span class="comment">//设置是否forward到下一个filter,默认为false</span></span><br><span class="line"><span class="comment">//            kaf.setForwardEvent(true);</span></span><br></pre></td></tr></table></figure>


<p>这句话的作用从源码中可以看出来，源码的sessionIdle方法后面有这么一段</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="keyword">this</span>.forwardEvent) &#123;</span><br><span class="line">    nextFilter.sessionIdle(session, status);<span class="comment">//如果设置为true，会将该状态往下传递</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<p>不知道大家还记不记得IoHandler里面也有sessionIdle方法，上面已经讲过，它的调用时机是通过setIdleTime来设置的，而setForwardEvent设置为false的话，则会将Idle事件拦截掉，不往下传递到IoHandle的sessionIdle，即该方法失效。</p>
<p>总的来说，就是设置了setRequestInterval的话，则设置setIdleTime将会失去意义。</p>
<p>以上是我的理解，如有谬误，恳请各位前辈指出。有不懂得也可以留言。</p>

    </div>

    
    
    
        

  <div class="followme">
    <p>欢迎关注我的其它发布渠道</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="/images/wechat_channel.jpg">
            <span class="icon">
              <i class="fab fa-weixin"></i>
            </span>

            <span class="label">WeChat</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">
          
          <div class="post-tags">
              <a href="/" rel="tag"><i class="fa fa-tag"></i> </a>
              <a href="/" rel="tag"><i class="fa fa-tag"></i> </a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2018/07/24/tech-2018-07-24-RPC/" rel="prev" title="RPC和RPC框架">
      <i class="fa fa-chevron-left"></i> RPC和RPC框架
    </a></div>
      <div class="post-nav-item">
    <a href="/2018/08/04/android-2018-08-04-DependenciesNote/" rel="next" title="Android依赖配置">
      Android依赖配置 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="header-overlay"></div>
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E5%BF%83%E8%B7%B3%E6%9C%BA%E5%88%B6"><span class="nav-number">1.</span> <span class="nav-text">一、心跳机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%8C%E3%80%81Mina%E5%AE%9E%E7%8E%B0%E5%BF%83%E8%B7%B3%E6%9C%BA%E5%88%B6"><span class="nav-number">2.</span> <span class="nav-text">二、Mina实现心跳机制</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="丘山月夜"
      src="/images/avatar.png">
  <p class="site-author-name" itemprop="name">丘山月夜</p>
  <div class="site-description" itemprop="description">君は空を見てるか,<br>風の音を聞いてるか</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">60</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">21</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">丘山月夜</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://mist.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Mist</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/muse.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
